#ifndef BLOCK_H
#define BLOCK_H
#include "DXUT.h"

#include "Settings.h"
#include "MathUtil.h"
#ifdef RENDER
#include "RenderInformation.h"
#endif
#include "Biomemap.h"
#include <map>
using namespace std;

#ifdef DEFERRED_UPDATE
extern SRWLOCK					DEFERRED_LOCK;
#endif

/* FACING */
#define FACEUP		0x01
#define FACEDOWN	0x02
#define FACEFRONT	0x04
#define FACEBACK	0x08
#define FACELEFT	0x10
#define FACERIGHT	0x20

#define FACEALL		0x3F
/* FACING */

/* STATUS */
#define AKTIV		0x40
#define INVERSE		0x80
/* STATUS */

/* UPDATES */
#define NO_UPADTE				0x00
#define	BLOCKUPDATE				0x01
#define STRONG_REDSTONEUPDATE	0x02
#define WEAK_REDSTONEUPDATE		0x04
#define GROWTH_UPDATE			0x08
#define BLOCK_PLACED			0x81
#define BLOCK_REMOVED			0x40
/* UPDATES */

/* BLOCKSTATUS */
#define LDV			0x0F
#define SOLID		0x10
#define LIQUID		0x20
#define STRONGPOWERED		0x40
#define WEAKPOWERED			0x80
#define POWERED				0xC0
/* BLOCKSTATUS */

/* CUSTOM */
#define AIR		0
#define WATER   100
#define LAVA    102
#define LEER	255
/* CUSTOM */

typedef byte blocktype;

struct Block;

struct genBuffer {
	bool face;
	bool liquid;

	byte id;
	byte skylight;
	byte blocklight;
	#pragma warning (disable : 4201 )
	union {
		unsigned char metadata;
		struct {
			unsigned char metaLo : 4;
			unsigned char metaHi : 4;
		};
	};

	float temperature;
	float humidity;
};

class IWorld
{
public:
	virtual Block	getBlock(int x, int z, int y)								= 0;
			Block	getBlock(D3DXVECTOR3 pos);
	virtual Block * getpBlock(int x, int z, int y)								= 0;
			Block * getpBlock(D3DXVECTOR3 pos);
	virtual void	setBlock(int x, int z, int y, Block block, byte flags = 0)	= 0;
			void	setBlock(D3DXVECTOR3 pos, Block block, byte flags = 0);
	virtual Block	breakBlock(int x, int z, int y)								= 0;
			Block	breakBlock(D3DXVECTOR3 pos);
#ifdef RENDER	
	virtual void	setDirty(int x, int z, int y, bool immediatly)				= 0;
			void	setDirty(D3DXVECTOR3 pos, bool immediatly);
#endif

	virtual void	update()													= 0;
	virtual byte	updateBlock(int x, int z, int y, byte type, byte * ticks)				= 0;
			byte	updateBlock(D3DXVECTOR3 pos, byte type, byte * ticks);
	virtual void	delayedUpdateBlock(int x, int z, int y, byte type, byte ticks)			= 0;
			void	delayedUpdateBlock(D3DXVECTOR3 pos, byte type, byte ticks);

	virtual float getTemperature(int x, int z)									= 0;
	virtual float getHumidity(int x, int z)										= 0;
	virtual BYTE  getBiome(int x, int z)										= 0;									

	bool ray(const D3DXVECTOR3 * eye, const D3DXVECTOR3 * dir, D3DXVECTOR3 * outCoord, Block * outBlock, bool placementRay = false, BYTE * facing = NULL);
};

/* Blockdefines */
extern const Block SKY_BLOCK;
extern const Block AIR_BLOCK;
extern const Block DIRT_BLOCK;
extern const Block GRASS_BLOCK;
extern const Block SNOWYGRASS_BLOCK;
extern const Block STONE_BLOCK;
extern const Block COBBLE_BLOCK;
extern const Block COAL_BLOCK;
extern const Block IRON_ORE_BLOCK;
extern const Block GOLD_ORE_BLOCK;
extern const Block DIAMAND_ORE_BLOCK;
extern const Block REDSTONE_ORE_BLOCK;
extern const Block WOOD_BLOCK;
extern const Block DARKWOOD_BLOCK;
extern const Block BIRCHWOOD_BLOCK;
extern const Block REDWOOD_BLOCK;

extern const Block LEAF_BLOCK;
extern const Block DARKLEAF_BLOCK;
extern const Block REDLEAF_BLOCK;
extern const Block TALLGRASS_BLOCK;
extern const Block GLOWSTONE_BLOCK;
extern const Block REED_BLOCK;
extern const Block CACTUS_BLOCK;

extern const Block SAND_BLOCK;

extern const Block TORCH;

extern const Block REDSTONE_DUST;
extern const Block REDSTONE_TORCH;

extern const Block STILLWATER_BLOCK;
extern const Block STILLLAVA_BLOCK;

extern const Block VOID_BLOCK;
/* Blockdefines */

#ifdef RENDER
typedef byte (*BLOCKGEN)(int,int,int,float,float,float,genBuffer*,BlockVertex*,WORD*);
#endif
typedef byte (*BLOCKUPD)(int*,int*,int*,IWorld*,Block,byte,byte*);
typedef pair<BlockVertex*, WORD> BLOCKGEO;

D3DXVECTOR2 * __fastcall getBlockUV(char blockID, char faceing, byte meta = 0);

struct Block
{
	/* Byte - 1 */
	blocktype blockID;
	/* Byte - 2 */

	unsigned char skyLight	 : 4;
	unsigned char blockLight : 4;

	/* Byte - 3 */
	#pragma warning (disable : 4201 )
	union {
		unsigned char metadata;
		struct {
			unsigned char metaLo : 4;
			unsigned char metaHi : 4;
		};
	};
	/* Byte - 4 */
	unsigned char status;

#ifdef RENDER
	inline genBuffer toBuffer() const {
		genBuffer buf = {
			!(status & SOLID),
			(status & LIQUID) == LIQUID,
			blockID,
			skyLight,
			blockLight,
			metadata,
			0.0f,
			0.0f,
		};	
		return buf;
	};
	inline genBuffer toBuffer(float t, float h) const {
		genBuffer buf = {
			!(status & SOLID),
			(status & LIQUID) == LIQUID,
			blockID,
			skyLight,
			blockLight,
			metadata,
			t,
			h,
		};	
		return buf;
	};

	static const map<blocktype, BLOCKGEN> generationMap;
	static       map<blocktype, BLOCKGEO> geometryMap;

	static INT Generate(byte blockID, int x, int z, int y, float px, float pz, float py, genBuffer * pBuffer, BlockVertex * pVertex, WORD * pSize);
	INT Generate(BlockVertex * pVertex, WORD * pSize) const;
	INT GenerateIndexed(BlockVertex ** pVertex, WORD * pVSize, WORD ** pIndex, WORD * pISize ) const;

#endif
	static const map<blocktype, BLOCKUPD> updateMap;


	BYTE Update(int * px, int * pz, int * py, IWorld * world, byte updateType, byte * ticks);
};

/* SET BLOCK DEFINES */
#define UPDATE_NONE			0x00
#define UPDATE_LIGHT		0x01
#define UPDATE_GEOMETRY		0x02
#define UPDATE_IMMEDIATLY	0x04
#define UPDATE_ALL			0x0F	
#define UPDATE_DELAY		0x10
#define UPDATE_IF_AIR		0x20
#define UPDATE_IFN_AIR		0x40
/* SET BLOCK DEFINES */

void					 blockUpdate(IWorld * world, DWORD random, Block block, int x, int z, int y);

#endif